#include "stdafx.h"
#include "rcocore.h"
#include "rcoimp/iodevice.h"
#include <QCoreApplication>
#include <QTextCodec>
#include <algorithm>

namespace rco{

namespace inner{
struct _global_init
{
	_global_init()
	{
		QTextCodec* codec = QTextCodec::codecForName("GBK");
		QTextCodec::setCodecForLocale(codec);
		QTextCodec::setCodecForCStrings(codec); 
		QTextCodec::setCodecForTr(codec);
	}
};

_global_init __g;
}

RcoCore::RcoCore(QObject* parent)
	: QTcpServer(parent)
{
	m_iTaskIdSeq = 0;
	m_bAutoCreateThread = true;
	m_bAcceptConn = true;
}

RcoCore::~RcoCore()
{

}

bool RcoCore::Init()
{
	m_cmdParser.Init();
	return true;
}

CmdParserResult RcoCore::OnCommandParsing(const QString& strCmd, RcoCommand* pRcoCmd, int* pPosParsed)
{
	Q_ASSERT(pRcoCmd && pPosParsed);
	return m_cmdParser.OnCommandParsing(strCmd, pRcoCmd, pPosParsed);
}

CmdParserResult RcoCore::OnCommandParsing(const wchar_t* str, RcoCommand* pRcoCmd, int* pPosParsed)
{
	Q_ASSERT(str && pPosParsed);
	return OnCommandParsing(QString::fromWCharArray(str), pRcoCmd, pPosParsed);
}

CmdParserResult RcoCore::OnCommandParsing(const char* str, RcoCommand* pRcoCmd, int* pPosParsed)
{
	Q_ASSERT(str && pPosParsed);
	return OnCommandParsing(QString::fromAscii(str), pRcoCmd, pPosParsed);
}

void RcoCore::incomingConnection(int socektId)
{
	if (!IsAcceptConnection())
		return;

	RcoIODevice* newConn = new RcoIODevice();
	connect(newConn, SIGNAL(BufferReady(const RcoCommandHead&, RcoBuffer&, RcoIODevice*)),
		this, SLOT(PickOneThreadToPursuit(const RcoCommandHead&, RcoBuffer&, RcoIODevice*)));
	newConn->setSocketDescriptor(socektId);
}

void RcoCore::SetAcceptConnection(bool bAccept)
{
	m_bAcceptConn = bAccept;
}

void RcoCore::SetAutoCreateThread(bool bCreate)
{
	m_bAutoCreateThread = bCreate;
}

bool RcoCore::IsAcceptConnection() const
{
	return m_bAcceptConn;
}

bool RcoCore::IsAutoCreateThread() const
{
	return m_bAutoCreateThread;
}

bool RcoCore::PickOneThreadToPursuit(const RcoCommandHead& rcoCmdHead, RcoBuffer& bytes, RcoIODevice* who)
{
	if (rcoCmdHead.ucCid == RCO_ID_REPORT)
	{
		emit TaskReport(rcoCmdHead.ucTid, bytes);
		return true;
	}

	RcoWorkThread* thread = nullptr;
	// Current task doesn't complete, go on
	if (rcoCmdHead.result == RCO_CR_CONTINUE)
	{
		thread = GetThread(rcoCmdHead.ucTid);
		goto _handle;
	}

	for (auto it = m_wkThreads.begin(); it != m_wkThreads.end(); ++it)
	{
		thread = *it;
		if (thread->SetIODevice(who))
			goto _handle;
	}

	if (IsAutoCreateThread())
	{
		AutoFreeWorkThread pAFThread(new RcoWorkThread);
		pAFThread->SetIODevice(who);

		// The Write Action of TcpSocket depends on QT event of MESSAGE LOOP. BUT work thread doesn't enter its own MESSAGE LOOP.
		// So the write action should pass to main thread which can enter its own MESSAGE LOOP.
		// CAUTION : 1. Connection type SHOULD BE Qt::QueuedConnection.
		//		     2. All the PARAMS should be alive/accessible before all slots' call ends.
		connect(pAFThread, SIGNAL(TaskCompleted(RcoWorkThread*, RcoCommandHead*, RcoBuffer*)), this, 
			SLOT(TaskDone(RcoWorkThread*, RcoCommandHead*, RcoBuffer*)), Qt::QueuedConnection);

//		connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

		m_wkThreads.push_back(pAFThread);
		thread = pAFThread.detach();
		thread->start();
	}

_handle:
	if (thread)
	{
		int iTaskId = rcoCmdHead.ucTid;
		// event will be deleted by the QT framework
		RcoWorkThreadData* data = new RcoWorkThreadData;
		data->rcoCmdHread = rcoCmdHead;

		if (iTaskId == 0)
		{
			data->iTaskId = ++m_iTaskIdSeq;
			data->rcoCmdHread.ucTid = m_iTaskIdSeq;
			m_TasksStatus.insert(m_iTaskIdSeq, qMakePair(thread, rcoCmdHead.result));
		}
		else
		{
			data->iTaskId = iTaskId;
		}

		data->bytes = bytes;
		QCoreApplication::postEvent(thread, data, Qt::HighEventPriority);
		return true;
	}

	return false;
}

void RcoCore::TaskDone(RcoWorkThread* thread, RcoCommandHead* rcoCmdHead, RcoBuffer* bytes)
{
	if (m_TasksStatus.size() >= 100)
	{
		for (auto it = m_TasksStatus.begin(); it != m_TasksStatus.end(); ++it)
		{
			if (it->second != RCO_CR_CONTINUE)
			{
				m_TasksStatus.remove(it.key());
				break;
			}
		}
	}

	auto it = m_TasksStatus.find(rcoCmdHead->ucTid);

	if (it != m_TasksStatus.end())
		it->second = rcoCmdHead->result;
}

RcoWorkThread* RcoCore::GetThread(int iTaskId)
{
	return FindItem(iTaskId).first;
}

CmdHandlerResult RcoCore::GetTaskStatus(int iTaskId)
{
	return FindItem(iTaskId).second;
}

RcoCore::RcoTaskItem RcoCore::FindItem(int iTaskId)
{
	auto it = m_TasksStatus.find(iTaskId);

	if (it == m_TasksStatus.end())
		return RcoTaskItem(nullptr, RCO_CR_CM_NOT_EXIST);
	else
		return *it;
}

}